package ws.very.util.stuct

trait DyTable extends Itb[DyMap] {
  def rows: Itb[DyMap]
  def iterator = rows.iterator
  def filterTb(fn: DyMap => B): DyTable
  def groupByTb[K](fn: DyMap => K): Map[K, DyTable]
  //FIXME:join left right full
  //FIXME:select

  def buildHashIndex(field: S) = HashIndexTable(groupByTb { _.str.selectDynamic(field) })

  def buildUqHashIndex(field: S) = UqHashIndexTable(groupByTb { _.str.selectDynamic(field) }.mapValues { _.head })
}
trait IIndexTable[T, M <: Map[S, T]] extends DyTable

case class UqHashIndexTable(index: Map[S, DyMap]) extends DyTable {
  def rows = index.values
  def filterTb(fn: DyMap => B) = UqHashIndexTable(index.filter { case (k, v) => fn(v) })
  def groupByTb[K](fn: DyMap => K) = index.groupBy { case (k, v) => fn(v) }.map {
    case (k, v) =>
      k -> UqHashIndexTable(v)
  }
  def query(eqs: Itb[S]) = UqHashIndexTable(eqs.map { t => t -> index.get(t) }.collect { case (t, Some(v)) => t -> v }.toMap)
  def get = index.get _
}
case class HashIndexTable(index: Map[S, DyTable]) extends DyTable {
  def rows = index.values.flatMap { locally }
  def filterTb(fn: DyMap => B) = HashIndexTable(index.mapValues { x => x.filterTb { fn } })
  def groupByTb[K](fn: DyMap => K) =
    index.mapValues { x => x.groupByTb { fn } }.toStream.flatMap {
      case (k, v) => v.toStream.map {
        case (i, v) =>
          (i -> (k, v))
      }
    }.groupBy { case (k, v) => k }.mapValues { v =>
      HashIndexTable(v.map { _._2 }.groupBy {
        case (k, _) =>
          k
      }.mapValues { t => MultDyTable(t.map { _._2 }) })
    }
  def query(eqs: Itb[S]) = HashIndexTable(eqs.map { t => t -> index.get(t) }.collect { case (t, Some(v)) => t -> v }.toMap)
  def get = index.get _
}

object HashIndexesTable {
  def build(old: DyTable)(uqFields: Itb[S] = Itb(), basicField: Itb[S] = Nil): HashIndexesTable = {
    val uqs = uqFields.map { f: S => f -> old.buildUqHashIndex(f) }.toMap
    val basics = basicField.map { f => f -> old.buildHashIndex(f) }.toMap

    HashIndexesTable(old, uqs, basics)
  }
}

case class HashIndexesTable(old: DyTable, uqIndexes: Map[S, UqHashIndexTable] = Map(), basicIndexes: Map[S, HashIndexTable] = Map()) extends DyTable {
  def rows = old.rows
  def filterTb(fn: DyMap => B) = old.filterTb { fn }
  def groupByTb[K](fn: DyMap => K) = old.groupByTb { fn }

  lazy val index = new {
    def uqQuery(field: S, eq: S) = uqIndexes.get(field).flatMap { _.index.get(eq) }
    def query(field: S, eq: S) = basicIndexes.get(field).flatMap { _.index.get(eq) }
    def uqsQuery(field: S, eqs: Itb[S]) = uqIndexes.get(field).map { _.query(eqs) }
    def querys(field: S, eqs: Itb[S]) = basicIndexes.get(field).map { _.query(eqs) }
  }

}

case class MultDyTable(tables: Itb[DyTable]) extends DyTable {
  def rows = tables.flatMap { _.rows }
  def filterTb(fn: DyMap => B) =
    MultDyTable(tables.map { _.filterTb { fn } })

  def groupByTb[K](fn: DyMap => K) = {
    val data = tables.map { _.groupByTb { fn } }
    data.flatMap { _.keySet }.map { key =>
      key -> MultDyTable(data.map { _.getOrElse(key, DyTable.empty) })
    }.toMap
  }

}

case class DyTableImpl(colNames: Itb[S], rowsData: Seq[Seq[A]]) extends DyTable {
  private val colsIndex = colNames.zipWithIndex.toMap
  lazy val rows = rowsData.toStream.map { data =>
    ResultRowImpl(colsIndex, data)
  }

  override def filter(fn: DyMap => B) =
    copy(colNames = this.colNames, rowsData = rows.filter { fn }.map { row =>
      row.values
    })

  def filterTb(fn: DyMap => B) = filter(fn)

  override def groupBy[K](fn: DyMap => K): Map[K, DyTableImpl] = {
    rows.groupBy { fn }.map {
      case (k, v) =>
        k -> DyTableImpl(colNames,
          v.map { row =>
            row.values
          })
    }
  }
  def groupByTb[K](fn: DyMap => K): Map[K, DyTable] = groupBy(fn)

  def select(fields: S*) = {
    val newFields = fields.filter { x => colNames.toSet.contains(x) }
    DyTableImpl(newFields, rows.map { x => x.select(newFields: _*).data })
  }

}

object DyTableImpl {
  val empty = DyTableImpl(Nil, Nil)
  def from(tb: DyTable) = {
    val cols = tb.toSet.flatMap { s: DyMap => s.keySet }.toSeq
    val rows = tb.toSeq.map { map =>
      cols.map { col =>
        map.selectDynamic(col)
      }
    }
    DyTableImpl(cols, rows)
  }
}
object DyTable {
  val empty = DyTableImpl.empty
}

case class DyMapTable(rowsData: Itb[Map[S, A]] = Nil) extends DyTable {
  lazy val rows = rowsData.toStream.map { data =>
    new DyMapImpl(data)
  }
  override def filter(fn: DyMap => B) = filterTb(fn)
  def filterTb(fn: DyMap => B) = DyMapTable(rows.filter { fn }.map { _.map })
  def groupByTb[K](fn: DyMap => K) = {
    rows.groupBy { fn }.map {
      case (k, v) =>
        k -> DyMapTable(v.map { t => t.map })
    }
  }

}



